/*/////////////////////////////////////////////////////////*/
/*  Copyright Christopher Kormanyos 2017 - 2018.           */
/*  Distributed under the Boost Software License,          */
/*  Version 1.0. (See accompanying file LICENSE_1_0.txt    */
/*  or copy at http://www.boost.org/LICENSE_1_0.txt)       */
/*/////////////////////////////////////////////////////////*/

#include <math/checksums/crc/Crc64.h>
#include <mcal_memory_progmem.h>

void Crc64_Initialize(Crc64_Context_Type* Crc_Context)
{
  /* Set the initial value. */
  Crc_Context->Crc_Value = UINT64_C(0xFFFFFFFFFFFFFFFF);
}

void Crc64_ProcessBytes(const uint8_t*      DataIn,
                        const size_t        DataLength,
                        Crc64_Context_Type* Crc_Context)
{
  size_t LoopCnt;

  /* Loop through the input data stream */
  for(LoopCnt = 0U; LoopCnt < DataLength; ++LoopCnt)
  {
    /* CRC-64/XZ Table based on 64-bit QWORDs. */

    static const uint64_t Crc64_Table[256U] MY_PROGMEM =
    {
      UINT64_C(0x0000000000000000), UINT64_C(0xB32E4CBE03A75F6F), UINT64_C(0xF4843657A840A05B), UINT64_C(0x47AA7AE9ABE7FF34), UINT64_C(0x7BD0C384FF8F5E33), UINT64_C(0xC8FE8F3AFC28015C), UINT64_C(0x8F54F5D357CFFE68), UINT64_C(0x3C7AB96D5468A107), 
      UINT64_C(0xF7A18709FF1EBC66), UINT64_C(0x448FCBB7FCB9E309), UINT64_C(0x0325B15E575E1C3D), UINT64_C(0xB00BFDE054F94352), UINT64_C(0x8C71448D0091E255), UINT64_C(0x3F5F08330336BD3A), UINT64_C(0x78F572DAA8D1420E), UINT64_C(0xCBDB3E64AB761D61), 
      UINT64_C(0x7D9BA13851336649), UINT64_C(0xCEB5ED8652943926), UINT64_C(0x891F976FF973C612), UINT64_C(0x3A31DBD1FAD4997D), UINT64_C(0x064B62BCAEBC387A), UINT64_C(0xB5652E02AD1B6715), UINT64_C(0xF2CF54EB06FC9821), UINT64_C(0x41E11855055BC74E), 
      UINT64_C(0x8A3A2631AE2DDA2F), UINT64_C(0x39146A8FAD8A8540), UINT64_C(0x7EBE1066066D7A74), UINT64_C(0xCD905CD805CA251B), UINT64_C(0xF1EAE5B551A2841C), UINT64_C(0x42C4A90B5205DB73), UINT64_C(0x056ED3E2F9E22447), UINT64_C(0xB6409F5CFA457B28), 
      UINT64_C(0xFB374270A266CC92), UINT64_C(0x48190ECEA1C193FD), UINT64_C(0x0FB374270A266CC9), UINT64_C(0xBC9D3899098133A6), UINT64_C(0x80E781F45DE992A1), UINT64_C(0x33C9CD4A5E4ECDCE), UINT64_C(0x7463B7A3F5A932FA), UINT64_C(0xC74DFB1DF60E6D95), 
      UINT64_C(0x0C96C5795D7870F4), UINT64_C(0xBFB889C75EDF2F9B), UINT64_C(0xF812F32EF538D0AF), UINT64_C(0x4B3CBF90F69F8FC0), UINT64_C(0x774606FDA2F72EC7), UINT64_C(0xC4684A43A15071A8), UINT64_C(0x83C230AA0AB78E9C), UINT64_C(0x30EC7C140910D1F3), 
      UINT64_C(0x86ACE348F355AADB), UINT64_C(0x3582AFF6F0F2F5B4), UINT64_C(0x7228D51F5B150A80), UINT64_C(0xC10699A158B255EF), UINT64_C(0xFD7C20CC0CDAF4E8), UINT64_C(0x4E526C720F7DAB87), UINT64_C(0x09F8169BA49A54B3), UINT64_C(0xBAD65A25A73D0BDC), 
      UINT64_C(0x710D64410C4B16BD), UINT64_C(0xC22328FF0FEC49D2), UINT64_C(0x85895216A40BB6E6), UINT64_C(0x36A71EA8A7ACE989), UINT64_C(0x0ADDA7C5F3C4488E), UINT64_C(0xB9F3EB7BF06317E1), UINT64_C(0xFE5991925B84E8D5), UINT64_C(0x4D77DD2C5823B7BA), 
      UINT64_C(0x64B62BCAEBC387A1), UINT64_C(0xD7986774E864D8CE), UINT64_C(0x90321D9D438327FA), UINT64_C(0x231C512340247895), UINT64_C(0x1F66E84E144CD992), UINT64_C(0xAC48A4F017EB86FD), UINT64_C(0xEBE2DE19BC0C79C9), UINT64_C(0x58CC92A7BFAB26A6), 
      UINT64_C(0x9317ACC314DD3BC7), UINT64_C(0x2039E07D177A64A8), UINT64_C(0x67939A94BC9D9B9C), UINT64_C(0xD4BDD62ABF3AC4F3), UINT64_C(0xE8C76F47EB5265F4), UINT64_C(0x5BE923F9E8F53A9B), UINT64_C(0x1C4359104312C5AF), UINT64_C(0xAF6D15AE40B59AC0), 
      UINT64_C(0x192D8AF2BAF0E1E8), UINT64_C(0xAA03C64CB957BE87), UINT64_C(0xEDA9BCA512B041B3), UINT64_C(0x5E87F01B11171EDC), UINT64_C(0x62FD4976457FBFDB), UINT64_C(0xD1D305C846D8E0B4), UINT64_C(0x96797F21ED3F1F80), UINT64_C(0x2557339FEE9840EF), 
      UINT64_C(0xEE8C0DFB45EE5D8E), UINT64_C(0x5DA24145464902E1), UINT64_C(0x1A083BACEDAEFDD5), UINT64_C(0xA9267712EE09A2BA), UINT64_C(0x955CCE7FBA6103BD), UINT64_C(0x267282C1B9C65CD2), UINT64_C(0x61D8F8281221A3E6), UINT64_C(0xD2F6B4961186FC89), 
      UINT64_C(0x9F8169BA49A54B33), UINT64_C(0x2CAF25044A02145C), UINT64_C(0x6B055FEDE1E5EB68), UINT64_C(0xD82B1353E242B407), UINT64_C(0xE451AA3EB62A1500), UINT64_C(0x577FE680B58D4A6F), UINT64_C(0x10D59C691E6AB55B), UINT64_C(0xA3FBD0D71DCDEA34), 
      UINT64_C(0x6820EEB3B6BBF755), UINT64_C(0xDB0EA20DB51CA83A), UINT64_C(0x9CA4D8E41EFB570E), UINT64_C(0x2F8A945A1D5C0861), UINT64_C(0x13F02D374934A966), UINT64_C(0xA0DE61894A93F609), UINT64_C(0xE7741B60E174093D), UINT64_C(0x545A57DEE2D35652), 
      UINT64_C(0xE21AC88218962D7A), UINT64_C(0x5134843C1B317215), UINT64_C(0x169EFED5B0D68D21), UINT64_C(0xA5B0B26BB371D24E), UINT64_C(0x99CA0B06E7197349), UINT64_C(0x2AE447B8E4BE2C26), UINT64_C(0x6D4E3D514F59D312), UINT64_C(0xDE6071EF4CFE8C7D), 
      UINT64_C(0x15BB4F8BE788911C), UINT64_C(0xA6950335E42FCE73), UINT64_C(0xE13F79DC4FC83147), UINT64_C(0x521135624C6F6E28), UINT64_C(0x6E6B8C0F1807CF2F), UINT64_C(0xDD45C0B11BA09040), UINT64_C(0x9AEFBA58B0476F74), UINT64_C(0x29C1F6E6B3E0301B), 
      UINT64_C(0xC96C5795D7870F42), UINT64_C(0x7A421B2BD420502D), UINT64_C(0x3DE861C27FC7AF19), UINT64_C(0x8EC62D7C7C60F076), UINT64_C(0xB2BC941128085171), UINT64_C(0x0192D8AF2BAF0E1E), UINT64_C(0x4638A2468048F12A), UINT64_C(0xF516EEF883EFAE45), 
      UINT64_C(0x3ECDD09C2899B324), UINT64_C(0x8DE39C222B3EEC4B), UINT64_C(0xCA49E6CB80D9137F), UINT64_C(0x7967AA75837E4C10), UINT64_C(0x451D1318D716ED17), UINT64_C(0xF6335FA6D4B1B278), UINT64_C(0xB199254F7F564D4C), UINT64_C(0x02B769F17CF11223), 
      UINT64_C(0xB4F7F6AD86B4690B), UINT64_C(0x07D9BA1385133664), UINT64_C(0x4073C0FA2EF4C950), UINT64_C(0xF35D8C442D53963F), UINT64_C(0xCF273529793B3738), UINT64_C(0x7C0979977A9C6857), UINT64_C(0x3BA3037ED17B9763), UINT64_C(0x888D4FC0D2DCC80C), 
      UINT64_C(0x435671A479AAD56D), UINT64_C(0xF0783D1A7A0D8A02), UINT64_C(0xB7D247F3D1EA7536), UINT64_C(0x04FC0B4DD24D2A59), UINT64_C(0x3886B22086258B5E), UINT64_C(0x8BA8FE9E8582D431), UINT64_C(0xCC0284772E652B05), UINT64_C(0x7F2CC8C92DC2746A), 
      UINT64_C(0x325B15E575E1C3D0), UINT64_C(0x8175595B76469CBF), UINT64_C(0xC6DF23B2DDA1638B), UINT64_C(0x75F16F0CDE063CE4), UINT64_C(0x498BD6618A6E9DE3), UINT64_C(0xFAA59ADF89C9C28C), UINT64_C(0xBD0FE036222E3DB8), UINT64_C(0x0E21AC88218962D7), 
      UINT64_C(0xC5FA92EC8AFF7FB6), UINT64_C(0x76D4DE52895820D9), UINT64_C(0x317EA4BB22BFDFED), UINT64_C(0x8250E80521188082), UINT64_C(0xBE2A516875702185), UINT64_C(0x0D041DD676D77EEA), UINT64_C(0x4AAE673FDD3081DE), UINT64_C(0xF9802B81DE97DEB1), 
      UINT64_C(0x4FC0B4DD24D2A599), UINT64_C(0xFCEEF8632775FAF6), UINT64_C(0xBB44828A8C9205C2), UINT64_C(0x086ACE348F355AAD), UINT64_C(0x34107759DB5DFBAA), UINT64_C(0x873E3BE7D8FAA4C5), UINT64_C(0xC094410E731D5BF1), UINT64_C(0x73BA0DB070BA049E), 
      UINT64_C(0xB86133D4DBCC19FF), UINT64_C(0x0B4F7F6AD86B4690), UINT64_C(0x4CE50583738CB9A4), UINT64_C(0xFFCB493D702BE6CB), UINT64_C(0xC3B1F050244347CC), UINT64_C(0x709FBCEE27E418A3), UINT64_C(0x3735C6078C03E797), UINT64_C(0x841B8AB98FA4B8F8), 
      UINT64_C(0xADDA7C5F3C4488E3), UINT64_C(0x1EF430E13FE3D78C), UINT64_C(0x595E4A08940428B8), UINT64_C(0xEA7006B697A377D7), UINT64_C(0xD60ABFDBC3CBD6D0), UINT64_C(0x6524F365C06C89BF), UINT64_C(0x228E898C6B8B768B), UINT64_C(0x91A0C532682C29E4), 
      UINT64_C(0x5A7BFB56C35A3485), UINT64_C(0xE955B7E8C0FD6BEA), UINT64_C(0xAEFFCD016B1A94DE), UINT64_C(0x1DD181BF68BDCBB1), UINT64_C(0x21AB38D23CD56AB6), UINT64_C(0x9285746C3F7235D9), UINT64_C(0xD52F0E859495CAED), UINT64_C(0x6601423B97329582), 
      UINT64_C(0xD041DD676D77EEAA), UINT64_C(0x636F91D96ED0B1C5), UINT64_C(0x24C5EB30C5374EF1), UINT64_C(0x97EBA78EC690119E), UINT64_C(0xAB911EE392F8B099), UINT64_C(0x18BF525D915FEFF6), UINT64_C(0x5F1528B43AB810C2), UINT64_C(0xEC3B640A391F4FAD), 
      UINT64_C(0x27E05A6E926952CC), UINT64_C(0x94CE16D091CE0DA3), UINT64_C(0xD3646C393A29F297), UINT64_C(0x604A2087398EADF8), UINT64_C(0x5C3099EA6DE60CFF), UINT64_C(0xEF1ED5546E415390), UINT64_C(0xA8B4AFBDC5A6ACA4), UINT64_C(0x1B9AE303C601F3CB), 
      UINT64_C(0x56ED3E2F9E224471), UINT64_C(0xE5C372919D851B1E), UINT64_C(0xA26908783662E42A), UINT64_C(0x114744C635C5BB45), UINT64_C(0x2D3DFDAB61AD1A42), UINT64_C(0x9E13B115620A452D), UINT64_C(0xD9B9CBFCC9EDBA19), UINT64_C(0x6A978742CA4AE576), 
      UINT64_C(0xA14CB926613CF817), UINT64_C(0x1262F598629BA778), UINT64_C(0x55C88F71C97C584C), UINT64_C(0xE6E6C3CFCADB0723), UINT64_C(0xDA9C7AA29EB3A624), UINT64_C(0x69B2361C9D14F94B), UINT64_C(0x2E184CF536F3067F), UINT64_C(0x9D36004B35545910), 
      UINT64_C(0x2B769F17CF112238), UINT64_C(0x9858D3A9CCB67D57), UINT64_C(0xDFF2A94067518263), UINT64_C(0x6CDCE5FE64F6DD0C), UINT64_C(0x50A65C93309E7C0B), UINT64_C(0xE388102D33392364), UINT64_C(0xA4226AC498DEDC50), UINT64_C(0x170C267A9B79833F), 
      UINT64_C(0xDCD7181E300F9E5E), UINT64_C(0x6FF954A033A8C131), UINT64_C(0x28532E49984F3E05), UINT64_C(0x9B7D62F79BE8616A), UINT64_C(0xA707DB9ACF80C06D), UINT64_C(0x14299724CC279F02), UINT64_C(0x5383EDCD67C06036), UINT64_C(0xE0ADA17364673F59), 
    };

    /* Perform the CRC-64/XZ algorithm. */

    const uint_fast8_t DataIndex =
      (uint_fast8_t) ((uint8_t) (  ((uint8_t) Crc_Context->Crc_Value)
                                 ^ ((uint8_t) DataIn[LoopCnt])));

    const uint64_t TableValue =
      mcal_memory_progmem_read_qword((mcal_progmem_uintptr_t) &Crc64_Table[DataIndex]);

    Crc_Context->Crc_Value = (uint64_t) (Crc_Context->Crc_Value >> 8U) ^ TableValue;
  }
}

void Crc64_Finalize(Crc64_Context_Type* Crc_Context)
{
  Crc_Context->Crc_Value ^= UINT64_C(0xFFFFFFFFFFFFFFFF);
}
